package com.peter.springcloud.eurekaconsumer.ribbon.hystrix.rest;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;

@RestController
public class DcController {
    private static Logger logger = LogManager.getLogger(ConsumerService.class);

    @Autowired
    RestTemplate restTemplate;
    @Autowired
    ConsumerService consumerService;

    // 演示“服务降级”。
    @GetMapping("/dc")
    public String dc() {
        return consumerService.consumer();
    }

    // 演示“服务熔断”。测试时间窗口内，超过制定数量的请求会触发熔断机制。
    @GetMapping("/dcs")
    public List<String> dcs() {
        List<String> list = new CopyOnWriteArrayList<String>();

        for(int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    list.add(consumerService.consumer());
                }
            }).start();
        }

        return list;
    }

    @HystrixCommand(fallbackMethod = "fallback")
    @GetMapping("/consumer")
    public String consumer() {
        return restTemplate.getForObject("http://eureka-client/dc", String.class);
    }

    /**
     * 服务降级的备用逻辑
     * 这种方式并不好，因为这个备用逻辑中，想要得到request等参数比较麻烦。
     * 推荐使用“定义消费服务”这种方式。（就如 ConsumerService ）。
     */
    public String fallback() {
        return "controller's fallback";
    }

    /**
     * 使用这个类模拟一次远程调用。
     */
    @Service
    class ConsumerService {
        @Autowired
        RestTemplate restTemplate;

        @HystrixCommand(fallbackMethod = "fallback", commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000") // 设置超时时间为 2 秒。
        })
        public String consumer() {
            // 打印处理该方法的线程名，以证实Hystrix会使用线程池处理该方法。
            logger.info("The Thread Name is " + Thread.currentThread().getName());

            // 可以在服务“eureka-client”的 /dc 请求的响应逻辑中添加 Thread.sleep() 方法，保证处理该请求的时间。一般触发“服务降级”或“熔断”。
            return restTemplate.getForObject("http://eureka-client/dc", String.class);
        }

        /**
         * 服务降级的备用逻辑
         */
        public String fallback() {
            return "service's fallback";
        }

    }
}
